"""
Author        yongfa
Date          2022-07-15 13:23:57
LastEditTime  2023-09-01 08:58:10
LastEditors   yongfa
Description   装饰器
"""

from functools import wraps
from flask import g, request, current_app
from threading import Thread
from flask_jwt_extended import get_current_user, get_jwt_identity, verify_jwt_in_request
from flask_jwt_extended.exceptions import NoAuthorizationError, InvalidHeaderError
from app.extensions.exception import UnauthorizedError, TokenException, TokenLogoutException, \
    TokenExpireException, Forbidden
from app.extensions.utils.enums import UserEnum
from app.extensions.utils import token_key
from app.extensions import redis_client, casbin_enforcer


def header_required(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        headers = request.headers
        scheme = headers.get('X-Forwarded-Proto', 'http')
        host = headers.get('Host', request.host)
        g.host_url = scheme + "://" + host
        return func(*args, **kwargs)
    return wrapper


def login_required(func):
    """登录验证装饰器"""
    @wraps(func)
    @header_required
    def wrapper(*args, **kwargs):
        if hasattr(g, 'user_id'):
            return func(*args, **kwargs)
        try:
            verify_jwt_in_request()
            # current_user = get_current_user()
        except NoAuthorizationError:
            raise UnauthorizedError()
        except InvalidHeaderError:
            raise TokenException()
        except Exception:
            raise TokenExpireException()

        auth_result = get_jwt_identity()
        if auth_result:
            user_id = auth_result.get('id')

            key = token_key(user_id)

            # 检测服务端缓存是否有这个token，因为修改密码和退出登录都会删除这个用户的key
            if redis_client.exists(key):
                g.user_id = user_id
                g.auth = auth_result.get('auth')
                g.roles = auth_result.get('roles')
                g.openid = auth_result.get('openid')
                g.is_super_admin = auth_result.get('is_super_admin')
                return func(*args, **kwargs)
            else:
                raise TokenLogoutException()

    return wrapper


def admin_required(func):
    @wraps(func)
    @login_required
    def inner(*args, **kwargs):
        auth = g.auth
        if auth == UserEnum.ADMIN.value:
            return func(*args, **kwargs)
        raise Forbidden()

    return inner


def permission_required(func):
    @wraps(func)
    @login_required
    def decorated_function(*args, **kwargs):
        api = {'endpoint': request.endpoint, 'method': request.method}
        if api not in g.apis:
            return Forbidden()
        return func(*args, **kwargs)
    return decorated_function


def casbin_required(func):
    @wraps(func)
    @login_required
    def decorated_function(*args, **kwargs):
        if g.is_super_admin:
            return func(*args, **kwargs)
        try:
            casbin_enforcer.load_policy()
        except Exception as e:
            current_app.log_exception("casbin 加载错误: ERROR: {}".format(e))
            casbin_enforcer.load_policy()

        result = False
        for role_id in g.roles:
            subject = str(role_id)
            object = request.path
            action = request.method
            # 重新加载casbin的策略 #TODO: 可以优化
            result = casbin_enforcer.enforce(subject, object, action)
            if result:
                break
        if not result:
            return Forbidden()
        return func(*args, **kwargs)
    return decorated_function


def async_thread(f):
    """多线程"""
    def inner(*args, **kwargs):
        app = current_app._get_current_object()
        kwargs['app'] = app
        Thread(target=f, args=args, kwargs=kwargs).start()
    return inner
